/** @file
  Main file for compression routine.

  Compression routine. The compression algorithm is a mixture of
  LZ77 and Huffman coding. LZ77 transforms the source data into a
  sequence of Original Characters and Pointers to repeated strings.
  This sequence is further divided into Blocks and Huffman codings
  are applied to each Block.

  Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
  SPDX-License-Identifier: BSD-2-Clause-Patent

**/
#include <Uefi.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/ShellLib.h>

#include "Compress.h"

//
// Macro Definitions
//
typedef INT16 NODE;
#define UINT8_MAX     0xff
#define UINT8_BIT     8
#define THRESHOLD     3
#define INIT_CRC      0
#define WNDBIT        13
#define WNDSIZ        (1U << WNDBIT)
#define MAXMATCH      256
#define BLKSIZ        (1U << 14)      // 16 * 1024U
#define PERC_FLAG     0x8000U
#define CODE_BIT      16
#define NIL           0
#define MAX_HASH_VAL  (3 * WNDSIZ + (WNDSIZ / 512 + 1) * UINT8_MAX)
#define HASH(LoopVar7, LoopVar5)  ((LoopVar7) + ((LoopVar5) << (WNDBIT - 9)) + WNDSIZ * 2)
#define CRCPOLY  0xA001
#define UPDATE_CRC(LoopVar5)  mCrc = mCrcTable[(mCrc ^ (LoopVar5)) & 0xFF] ^ (mCrc >> UINT8_BIT)

//
// C: the Char&Len Set; P: the Position Set; T: the exTra Set
//
#define NC    (UINT8_MAX + MAXMATCH + 2 - THRESHOLD)
#define CBIT  9
#define NP    (WNDBIT + 1)
#define PBIT  4
#define NT    (CODE_BIT + 3)
#define TBIT  5
#if NT > NP
#define                 NPT  NT
#else
#define                 NPT  NP
#endif
//
// Function Prototypes
//

/**
  Put a dword to output stream

  @param[in] Data    The dword to put.
**/
VOID
PutDword (
  IN UINT32  Data
  );

//
//  Global Variables
//
STATIC UINT8  *mSrc;
STATIC UINT8  *mDst;
STATIC UINT8  *mSrcUpperLimit;
STATIC UINT8  *mDstUpperLimit;

STATIC UINT8   *mLevel;
STATIC UINT8   *mText;
STATIC UINT8   *mChildCount;
STATIC UINT8   *mBuf;
STATIC UINT8   mCLen[NC];
STATIC UINT8   mPTLen[NPT];
STATIC UINT8   *mLen;
STATIC INT16   mHeap[NC + 1];
STATIC INT32   mRemainder;
STATIC INT32   mMatchLen;
STATIC INT32   mBitCount;
STATIC INT32   mHeapSize;
STATIC INT32   mTempInt32;
STATIC UINT32  mBufSiz = 0;
STATIC UINT32  mOutputPos;
STATIC UINT32  mOutputMask;
STATIC UINT32  mSubBitBuf;
STATIC UINT32  mCrc;
STATIC UINT32  mCompSize;
STATIC UINT32  mOrigSize;

STATIC UINT16  *mFreq;
STATIC UINT16  *mSortPtr;
STATIC UINT16  mLenCnt[17];
STATIC UINT16  mLeft[2 * NC - 1];
STATIC UINT16  mRight[2 * NC - 1];
STATIC UINT16  mCrcTable[UINT8_MAX + 1];
STATIC UINT16  mCFreq[2 * NC - 1];
STATIC UINT16  mCCode[NC];
STATIC UINT16  mPFreq[2 * NP - 1];
STATIC UINT16  mPTCode[NPT];
STATIC UINT16  mTFreq[2 * NT - 1];

STATIC NODE  mPos;
STATIC NODE  mMatchPos;
STATIC NODE  mAvail;
STATIC NODE  *mPosition;
STATIC NODE  *mParent;
STATIC NODE  *mPrev;
STATIC NODE  *mNext        = NULL;
INT32        mHuffmanDepth = 0;

/**
  Make a CRC table.

**/
VOID
MakeCrcTable (
  VOID
  )
{
  UINT32  LoopVar1;

  UINT32  LoopVar2;

  UINT32  LoopVar4;

  for (LoopVar1 = 0; LoopVar1 <= UINT8_MAX; LoopVar1++) {
    LoopVar4 = LoopVar1;
    for (LoopVar2 = 0; LoopVar2 < UINT8_BIT; LoopVar2++) {
      if ((LoopVar4 & 1) != 0) {
        LoopVar4 = (LoopVar4 >> 1) ^ CRCPOLY;
      } else {
        LoopVar4 >>= 1;
      }
    }

    mCrcTable[LoopVar1] = (UINT16)LoopVar4;
  }
}

/**
  Put a dword to output stream

  @param[in] Data    The dword to put.
**/
VOID
PutDword (
  IN UINT32  Data
  )
{
  if (mDst < mDstUpperLimit) {
    *mDst++ = (UINT8)(((UINT8)(Data)) & 0xff);
  }

  if (mDst < mDstUpperLimit) {
    *mDst++ = (UINT8)(((UINT8)(Data >> 0x08)) & 0xff);
  }

  if (mDst < mDstUpperLimit) {
    *mDst++ = (UINT8)(((UINT8)(Data >> 0x10)) & 0xff);
  }

  if (mDst < mDstUpperLimit) {
    *mDst++ = (UINT8)(((UINT8)(Data >> 0x18)) & 0xff);
  }
}

/**
  Allocate memory spaces for data structures used in compression process.

  @retval EFI_SUCCESS           Memory was allocated successfully.
  @retval EFI_OUT_OF_RESOURCES  A memory allocation failed.
**/
EFI_STATUS
AllocateMemory (
  VOID
  )
{
  mText       = AllocateZeroPool (WNDSIZ * 2 + MAXMATCH);
  mLevel      = AllocateZeroPool ((WNDSIZ + UINT8_MAX + 1) * sizeof (*mLevel));
  mChildCount = AllocateZeroPool ((WNDSIZ + UINT8_MAX + 1) * sizeof (*mChildCount));
  mPosition   = AllocateZeroPool ((WNDSIZ + UINT8_MAX + 1) * sizeof (*mPosition));
  mParent     = AllocateZeroPool (WNDSIZ * 2 * sizeof (*mParent));
  mPrev       = AllocateZeroPool (WNDSIZ * 2 * sizeof (*mPrev));
  mNext       = AllocateZeroPool ((MAX_HASH_VAL + 1) * sizeof (*mNext));

  mBufSiz = BLKSIZ;
  mBuf    = AllocateZeroPool (mBufSiz);
  while (mBuf == NULL) {
    mBufSiz = (mBufSiz / 10U) * 9U;
    if (mBufSiz < 4 * 1024U) {
      return EFI_OUT_OF_RESOURCES;
    }

    mBuf = AllocateZeroPool (mBufSiz);
  }

  mBuf[0] = 0;

  return EFI_SUCCESS;
}

/**
  Called when compression is completed to free memory previously allocated.

**/
VOID
FreeMemory (
  VOID
  )
{
  SHELL_FREE_NON_NULL (mText);
  SHELL_FREE_NON_NULL (mLevel);
  SHELL_FREE_NON_NULL (mChildCount);
  SHELL_FREE_NON_NULL (mPosition);
  SHELL_FREE_NON_NULL (mParent);
  SHELL_FREE_NON_NULL (mPrev);
  SHELL_FREE_NON_NULL (mNext);
  SHELL_FREE_NON_NULL (mBuf);
}

/**
  Initialize String Info Log data structures.
**/
VOID
InitSlide (
  VOID
  )
{
  NODE  LoopVar1;

  SetMem (mLevel + WNDSIZ, (UINT8_MAX + 1) * sizeof (UINT8), 1);
  SetMem (mPosition + WNDSIZ, (UINT8_MAX + 1) * sizeof (NODE), 0);

  SetMem (mParent + WNDSIZ, WNDSIZ * sizeof (NODE), 0);

  mAvail = 1;
  for (LoopVar1 = 1; LoopVar1 < WNDSIZ - 1; LoopVar1++) {
    mNext[LoopVar1] = (NODE)(LoopVar1 + 1);
  }

  mNext[WNDSIZ - 1] = NIL;
  SetMem (mNext + WNDSIZ * 2, (MAX_HASH_VAL - WNDSIZ * 2 + 1) * sizeof (NODE), 0);
}

/**
  Find child node given the parent node and the edge character

  @param[in] LoopVar6       The parent node.
  @param[in] LoopVar5       The edge character.

  @return             The child node.
  @retval NIL(Zero)   No child could be found.

**/
NODE
Child (
  IN NODE   LoopVar6,
  IN UINT8  LoopVar5
  )
{
  NODE  LoopVar4;

  LoopVar4     = mNext[HASH (LoopVar6, LoopVar5)];
  mParent[NIL] = LoopVar6;   /* sentinel */
  while (mParent[LoopVar4] != LoopVar6) {
    LoopVar4 = mNext[LoopVar4];
  }

  return LoopVar4;
}

/**
  Create a new child for a given parent node.

  @param[in] LoopVar6       The parent node.
  @param[in] LoopVar5       The edge character.
  @param[in] LoopVar4       The child node.
**/
VOID
MakeChild (
  IN NODE   LoopVar6,
  IN UINT8  LoopVar5,
  IN NODE   LoopVar4
  )
{
  NODE  LoopVar12;

  NODE  LoopVar10;

  LoopVar12         = (NODE)HASH (LoopVar6, LoopVar5);
  LoopVar10         = mNext[LoopVar12];
  mNext[LoopVar12]  = LoopVar4;
  mNext[LoopVar4]   = LoopVar10;
  mPrev[LoopVar10]  = LoopVar4;
  mPrev[LoopVar4]   = LoopVar12;
  mParent[LoopVar4] = LoopVar6;
  mChildCount[LoopVar6]++;
}

/**
  Split a node.

  @param[in] Old     The node to split.
**/
VOID
Split (
  IN NODE  Old
  )
{
  NODE  New;

  NODE  LoopVar10;

  New              = mAvail;
  mAvail           = mNext[New];
  mChildCount[New] = 0;
  LoopVar10        = mPrev[Old];
  mPrev[New]       = LoopVar10;
  mNext[LoopVar10] = New;
  LoopVar10        = mNext[Old];
  mNext[New]       = LoopVar10;
  mPrev[LoopVar10] = New;
  mParent[New]     = mParent[Old];
  mLevel[New]      = (UINT8)mMatchLen;
  mPosition[New]   = mPos;
  MakeChild (New, mText[mMatchPos + mMatchLen], Old);
  MakeChild (New, mText[mPos + mMatchLen], mPos);
}

/**
  Insert string info for current position into the String Info Log.

**/
VOID
InsertNode (
  VOID
  )
{
  NODE  LoopVar6;

  NODE  LoopVar4;

  NODE  LoopVar2;

  NODE   LoopVar10;
  UINT8  LoopVar5;
  UINT8  *TempString3;
  UINT8  *TempString2;

  if (mMatchLen >= 4) {
    //
    // We have just got a long match, the target tree
    // can be located by MatchPos + 1. Travese the tree
    // from bottom up to get to a proper starting point.
    // The usage of PERC_FLAG ensures proper node deletion
    // in DeleteNode() later.
    //
    mMatchLen--;
    LoopVar4 = (NODE)((mMatchPos + 1) | WNDSIZ);
    LoopVar6 = mParent[LoopVar4];
    while (LoopVar6 == NIL) {
      LoopVar4 = mNext[LoopVar4];
      LoopVar6 = mParent[LoopVar4];
    }

    while (mLevel[LoopVar6] >= mMatchLen) {
      LoopVar4 = LoopVar6;
      LoopVar6 = mParent[LoopVar6];
    }

    LoopVar10 = LoopVar6;
    while (mPosition[LoopVar10] < 0) {
      mPosition[LoopVar10] = mPos;
      LoopVar10            = mParent[LoopVar10];
    }

    if (LoopVar10 < WNDSIZ) {
      mPosition[LoopVar10] = (NODE)(mPos | PERC_FLAG);
    }
  } else {
    //
    // Locate the target tree
    //
    LoopVar6 = (NODE)(mText[mPos] + WNDSIZ);
    LoopVar5 = mText[mPos + 1];
    LoopVar4 = Child (LoopVar6, LoopVar5);
    if (LoopVar4 == NIL) {
      MakeChild (LoopVar6, LoopVar5, mPos);
      mMatchLen = 1;
      return;
    }

    mMatchLen = 2;
  }

  //
  // Traverse down the tree to find a match.
  // Update Position value along the route.
  // Node split or creation is involved.
  //
  for ( ; ;) {
    if (LoopVar4 >= WNDSIZ) {
      LoopVar2  = MAXMATCH;
      mMatchPos = LoopVar4;
    } else {
      LoopVar2  = mLevel[LoopVar4];
      mMatchPos = (NODE)(mPosition[LoopVar4] & ~PERC_FLAG);
    }

    if (mMatchPos >= mPos) {
      mMatchPos -= WNDSIZ;
    }

    TempString3 = &mText[mPos + mMatchLen];
    TempString2 = &mText[mMatchPos + mMatchLen];
    while (mMatchLen < LoopVar2) {
      if (*TempString3 != *TempString2) {
        Split (LoopVar4);
        return;
      }

      mMatchLen++;
      TempString3++;
      TempString2++;
    }

    if (mMatchLen >= MAXMATCH) {
      break;
    }

    mPosition[LoopVar4] = mPos;
    LoopVar6            = LoopVar4;
    LoopVar4            = Child (LoopVar6, *TempString3);
    if (LoopVar4 == NIL) {
      MakeChild (LoopVar6, *TempString3, mPos);
      return;
    }

    mMatchLen++;
  }

  LoopVar10         = mPrev[LoopVar4];
  mPrev[mPos]       = LoopVar10;
  mNext[LoopVar10]  = mPos;
  LoopVar10         = mNext[LoopVar4];
  mNext[mPos]       = LoopVar10;
  mPrev[LoopVar10]  = mPos;
  mParent[mPos]     = LoopVar6;
  mParent[LoopVar4] = NIL;

  //
  // Special usage of 'next'
  //
  mNext[LoopVar4] = mPos;
}

/**
  Delete outdated string info. (The Usage of PERC_FLAG
  ensures a clean deletion).

**/
VOID
DeleteNode (
  VOID
  )
{
  NODE  LoopVar6;

  NODE  LoopVar4;

  NODE  LoopVar11;

  NODE  LoopVar10;

  NODE  LoopVar9;

  if (mParent[mPos] == NIL) {
    return;
  }

  LoopVar4         = mPrev[mPos];
  LoopVar11        = mNext[mPos];
  mNext[LoopVar4]  = LoopVar11;
  mPrev[LoopVar11] = LoopVar4;
  LoopVar4         = mParent[mPos];
  mParent[mPos]    = NIL;
  if (LoopVar4 >= WNDSIZ) {
    return;
  }

  mChildCount[LoopVar4]--;
  if (mChildCount[LoopVar4] > 1) {
    return;
  }

  LoopVar10 = (NODE)(mPosition[LoopVar4] & ~PERC_FLAG);
  if (LoopVar10 >= mPos) {
    LoopVar10 -= WNDSIZ;
  }

  LoopVar11 = LoopVar10;
  LoopVar6  = mParent[LoopVar4];
  LoopVar9  = mPosition[LoopVar6];
  while ((LoopVar9 & PERC_FLAG) != 0) {
    LoopVar9 &= ~PERC_FLAG;
    if (LoopVar9 >= mPos) {
      LoopVar9 -= WNDSIZ;
    }

    if (LoopVar9 > LoopVar11) {
      LoopVar11 = LoopVar9;
    }

    mPosition[LoopVar6] = (NODE)(LoopVar11 | WNDSIZ);
    LoopVar6            = mParent[LoopVar6];
    LoopVar9            = mPosition[LoopVar6];
  }

  if (LoopVar6 < WNDSIZ) {
    if (LoopVar9 >= mPos) {
      LoopVar9 -= WNDSIZ;
    }

    if (LoopVar9 > LoopVar11) {
      LoopVar11 = LoopVar9;
    }

    mPosition[LoopVar6] = (NODE)(LoopVar11 | WNDSIZ | PERC_FLAG);
  }

  LoopVar11          = Child (LoopVar4, mText[LoopVar10 + mLevel[LoopVar4]]);
  LoopVar10          = mPrev[LoopVar11];
  LoopVar9           = mNext[LoopVar11];
  mNext[LoopVar10]   = LoopVar9;
  mPrev[LoopVar9]    = LoopVar10;
  LoopVar10          = mPrev[LoopVar4];
  mNext[LoopVar10]   = LoopVar11;
  mPrev[LoopVar11]   = LoopVar10;
  LoopVar10          = mNext[LoopVar4];
  mPrev[LoopVar10]   = LoopVar11;
  mNext[LoopVar11]   = LoopVar10;
  mParent[LoopVar11] = mParent[LoopVar4];
  mParent[LoopVar4]  = NIL;
  mNext[LoopVar4]    = mAvail;
  mAvail             = LoopVar4;
}

/**
  Read in source data

  @param[out] LoopVar7   The buffer to hold the data.
  @param[in] LoopVar8    The number of bytes to read.

  @return The number of bytes actually read.
**/
INT32
FreadCrc (
  OUT UINT8  *LoopVar7,
  IN  INT32  LoopVar8
  )
{
  INT32  LoopVar1;

  for (LoopVar1 = 0; mSrc < mSrcUpperLimit && LoopVar1 < LoopVar8; LoopVar1++) {
    *LoopVar7++ = *mSrc++;
  }

  LoopVar8 = LoopVar1;

  LoopVar7  -= LoopVar8;
  mOrigSize += LoopVar8;
  LoopVar1--;
  while (LoopVar1 >= 0) {
    UPDATE_CRC (*LoopVar7++);
    LoopVar1--;
  }

  return LoopVar8;
}

/**
  Advance the current position (read in new data if needed).
  Delete outdated string info. Find a match string for current position.

  @retval TRUE      The operation was successful.
  @retval FALSE     The operation failed due to insufficient memory.
**/
BOOLEAN
GetNextMatch (
  VOID
  )
{
  INT32  LoopVar8;
  VOID   *Temp;

  mRemainder--;
  mPos++;
  if (mPos == WNDSIZ * 2) {
    Temp = AllocateZeroPool (WNDSIZ + MAXMATCH);
    if (Temp == NULL) {
      return (FALSE);
    }

    CopyMem (Temp, &mText[WNDSIZ], WNDSIZ + MAXMATCH);
    CopyMem (&mText[0], Temp, WNDSIZ + MAXMATCH);
    FreePool (Temp);
    LoopVar8    = FreadCrc (&mText[WNDSIZ + MAXMATCH], WNDSIZ);
    mRemainder += LoopVar8;
    mPos        = WNDSIZ;
  }

  DeleteNode ();
  InsertNode ();

  return (TRUE);
}

/**
  Send entry LoopVar1 down the queue.

  @param[in] LoopVar1    The index of the item to move.
**/
VOID
DownHeap (
  IN INT32  i
  )
{
  INT32  LoopVar1;

  INT32  LoopVar2;

  //
  // priority queue: send i-th entry down heap
  //
  LoopVar2 = mHeap[i];
  LoopVar1 = 2 * i;
  while (LoopVar1 <= mHeapSize) {
    if ((LoopVar1 < mHeapSize) && (mFreq[mHeap[LoopVar1]] > mFreq[mHeap[LoopVar1 + 1]])) {
      LoopVar1++;
    }

    if (mFreq[LoopVar2] <= mFreq[mHeap[LoopVar1]]) {
      break;
    }

    mHeap[i] = mHeap[LoopVar1];
    i        = LoopVar1;
    LoopVar1 = 2 * i;
  }

  mHeap[i] = (INT16)LoopVar2;
}

/**
  Count the number of each code length for a Huffman tree.

  @param[in] LoopVar1      The top node.
**/
VOID
CountLen (
  IN INT32  LoopVar1
  )
{
  if (LoopVar1 < mTempInt32) {
    mLenCnt[(mHuffmanDepth < 16) ? mHuffmanDepth : 16]++;
  } else {
    mHuffmanDepth++;
    CountLen (mLeft[LoopVar1]);
    CountLen (mRight[LoopVar1]);
    mHuffmanDepth--;
  }
}

/**
  Create code length array for a Huffman tree.

  @param[in] Root   The root of the tree.
**/
VOID
MakeLen (
  IN INT32  Root
  )
{
  INT32  LoopVar1;

  INT32   LoopVar2;
  UINT32  Cum;

  for (LoopVar1 = 0; LoopVar1 <= 16; LoopVar1++) {
    mLenCnt[LoopVar1] = 0;
  }

  CountLen (Root);

  //
  // Adjust the length count array so that
  // no code will be generated longer than its designated length
  //
  Cum = 0;
  for (LoopVar1 = 16; LoopVar1 > 0; LoopVar1--) {
    Cum += mLenCnt[LoopVar1] << (16 - LoopVar1);
  }

  while (Cum != (1U << 16)) {
    mLenCnt[16]--;
    for (LoopVar1 = 15; LoopVar1 > 0; LoopVar1--) {
      if (mLenCnt[LoopVar1] != 0) {
        mLenCnt[LoopVar1]--;
        mLenCnt[LoopVar1 + 1] += 2;
        break;
      }
    }

    Cum--;
  }

  for (LoopVar1 = 16; LoopVar1 > 0; LoopVar1--) {
    LoopVar2 = mLenCnt[LoopVar1];
    LoopVar2--;
    while (LoopVar2 >= 0) {
      mLen[*mSortPtr++] = (UINT8)LoopVar1;
      LoopVar2--;
    }
  }
}

/**
  Assign code to each symbol based on the code length array.

  @param[in] LoopVar8      The number of symbols.
  @param[in] Len    The code length array.
  @param[out] Code  The stores codes for each symbol.
**/
VOID
MakeCode (
  IN  INT32   LoopVar8,
  IN  UINT8   Len[],
  OUT UINT16  Code[]
  )
{
  INT32   LoopVar1;
  UINT16  Start[18];

  Start[1] = 0;
  for (LoopVar1 = 1; LoopVar1 <= 16; LoopVar1++) {
    Start[LoopVar1 + 1] = (UINT16)((Start[LoopVar1] + mLenCnt[LoopVar1]) << 1);
  }

  for (LoopVar1 = 0; LoopVar1 < LoopVar8; LoopVar1++) {
    Code[LoopVar1] = Start[Len[LoopVar1]]++;
  }
}

/**
  Generates Huffman codes given a frequency distribution of symbols.

  @param[in] NParm      The number of symbols.
  @param[in] FreqParm   The frequency of each symbol.
  @param[out] LenParm   The code length for each symbol.
  @param[out] CodeParm  The code for each symbol.

  @return The root of the Huffman tree.
**/
INT32
MakeTree (
  IN  INT32   NParm,
  IN  UINT16  FreqParm[],
  OUT UINT8   LenParm[],
  OUT UINT16  CodeParm[]
  )
{
  INT32  LoopVar1;

  INT32  LoopVar2;

  INT32  LoopVar3;

  INT32  Avail;

  //
  // make tree, calculate len[], return root
  //
  mTempInt32 = NParm;
  mFreq      = FreqParm;
  mLen       = LenParm;
  Avail      = mTempInt32;
  mHeapSize  = 0;
  mHeap[1]   = 0;
  for (LoopVar1 = 0; LoopVar1 < mTempInt32; LoopVar1++) {
    mLen[LoopVar1] = 0;
    if ((mFreq[LoopVar1]) != 0) {
      mHeapSize++;
      mHeap[mHeapSize] = (INT16)LoopVar1;
    }
  }

  if (mHeapSize < 2) {
    CodeParm[mHeap[1]] = 0;
    return mHeap[1];
  }

  for (LoopVar1 = mHeapSize / 2; LoopVar1 >= 1; LoopVar1--) {
    //
    // make priority queue
    //
    DownHeap (LoopVar1);
  }

  mSortPtr = CodeParm;
  do {
    LoopVar1 = mHeap[1];
    if (LoopVar1 < mTempInt32) {
      *mSortPtr++ = (UINT16)LoopVar1;
    }

    mHeap[1] = mHeap[mHeapSize--];
    DownHeap (1);
    LoopVar2 = mHeap[1];
    if (LoopVar2 < mTempInt32) {
      *mSortPtr++ = (UINT16)LoopVar2;
    }

    LoopVar3        = Avail++;
    mFreq[LoopVar3] = (UINT16)(mFreq[LoopVar1] + mFreq[LoopVar2]);
    mHeap[1]        = (INT16)LoopVar3;
    DownHeap (1);
    mLeft[LoopVar3]  = (UINT16)LoopVar1;
    mRight[LoopVar3] = (UINT16)LoopVar2;
  } while (mHeapSize > 1);

  mSortPtr = CodeParm;
  MakeLen (LoopVar3);
  MakeCode (NParm, LenParm, CodeParm);

  //
  // return root
  //
  return LoopVar3;
}

/**
  Outputs rightmost LoopVar8 bits of x

  @param[in] LoopVar8   The rightmost LoopVar8 bits of the data is used.
  @param[in] x   The data.
**/
VOID
PutBits (
  IN INT32   LoopVar8,
  IN UINT32  x
  )
{
  UINT8  Temp;

  if (LoopVar8 < mBitCount) {
    mSubBitBuf |= x << (mBitCount -= LoopVar8);
  } else {
    Temp = (UINT8)(mSubBitBuf | (x >> (LoopVar8 -= mBitCount)));
    if (mDst < mDstUpperLimit) {
      *mDst++ = Temp;
    }

    mCompSize++;

    if (LoopVar8 < UINT8_BIT) {
      mSubBitBuf = x << (mBitCount = UINT8_BIT - LoopVar8);
    } else {
      Temp = (UINT8)(x >> (LoopVar8 - UINT8_BIT));
      if (mDst < mDstUpperLimit) {
        *mDst++ = Temp;
      }

      mCompSize++;

      mSubBitBuf = x << (mBitCount = 2 * UINT8_BIT - LoopVar8);
    }
  }
}

/**
  Encode a signed 32 bit number.

  @param[in] LoopVar5     The number to encode.
**/
VOID
EncodeC (
  IN INT32  LoopVar5
  )
{
  PutBits (mCLen[LoopVar5], mCCode[LoopVar5]);
}

/**
  Encode a unsigned 32 bit number.

  @param[in] LoopVar7     The number to encode.
**/
VOID
EncodeP (
  IN UINT32  LoopVar7
  )
{
  UINT32  LoopVar5;

  UINT32  LoopVar6;

  LoopVar5 = 0;
  LoopVar6 = LoopVar7;
  while (LoopVar6 != 0) {
    LoopVar6 >>= 1;
    LoopVar5++;
  }

  PutBits (mPTLen[LoopVar5], mPTCode[LoopVar5]);
  if (LoopVar5 > 1) {
    PutBits (LoopVar5 - 1, LoopVar7 & (0xFFFFU >> (17 - LoopVar5)));
  }
}

/**
  Count the frequencies for the Extra Set.

**/
VOID
CountTFreq (
  VOID
  )
{
  INT32  LoopVar1;

  INT32  LoopVar3;

  INT32  LoopVar8;

  INT32  Count;

  for (LoopVar1 = 0; LoopVar1 < NT; LoopVar1++) {
    mTFreq[LoopVar1] = 0;
  }

  LoopVar8 = NC;
  while (LoopVar8 > 0 && mCLen[LoopVar8 - 1] == 0) {
    LoopVar8--;
  }

  LoopVar1 = 0;
  while (LoopVar1 < LoopVar8) {
    LoopVar3 = mCLen[LoopVar1++];
    if (LoopVar3 == 0) {
      Count = 1;
      while (LoopVar1 < LoopVar8 && mCLen[LoopVar1] == 0) {
        LoopVar1++;
        Count++;
      }

      if (Count <= 2) {
        mTFreq[0] = (UINT16)(mTFreq[0] + Count);
      } else if (Count <= 18) {
        mTFreq[1]++;
      } else if (Count == 19) {
        mTFreq[0]++;
        mTFreq[1]++;
      } else {
        mTFreq[2]++;
      }
    } else {
      ASSERT ((LoopVar3+2) < (2 * NT - 1));
      mTFreq[LoopVar3 + 2]++;
    }
  }
}

/**
  Outputs the code length array for the Extra Set or the Position Set.

  @param[in] LoopVar8       The number of symbols.
  @param[in] nbit           The number of bits needed to represent 'LoopVar8'.
  @param[in] Special        The special symbol that needs to be take care of.

**/
VOID
WritePTLen (
  IN INT32  LoopVar8,
  IN INT32  nbit,
  IN INT32  Special
  )
{
  INT32  LoopVar1;

  INT32  LoopVar3;

  while (LoopVar8 > 0 && mPTLen[LoopVar8 - 1] == 0) {
    LoopVar8--;
  }

  PutBits (nbit, LoopVar8);
  LoopVar1 = 0;
  while (LoopVar1 < LoopVar8) {
    LoopVar3 = mPTLen[LoopVar1++];
    if (LoopVar3 <= 6) {
      PutBits (3, LoopVar3);
    } else {
      PutBits (LoopVar3 - 3, (1U << (LoopVar3 - 3)) - 2);
    }

    if (LoopVar1 == Special) {
      while (LoopVar1 < 6 && mPTLen[LoopVar1] == 0) {
        LoopVar1++;
      }

      PutBits (2, (LoopVar1 - 3) & 3);
    }
  }
}

/**
  Outputs the code length array for Char&Length Set.
**/
VOID
WriteCLen (
  VOID
  )
{
  INT32  LoopVar1;

  INT32  LoopVar3;

  INT32  LoopVar8;

  INT32  Count;

  LoopVar8 = NC;
  while (LoopVar8 > 0 && mCLen[LoopVar8 - 1] == 0) {
    LoopVar8--;
  }

  PutBits (CBIT, LoopVar8);
  LoopVar1 = 0;
  while (LoopVar1 < LoopVar8) {
    LoopVar3 = mCLen[LoopVar1++];
    if (LoopVar3 == 0) {
      Count = 1;
      while (LoopVar1 < LoopVar8 && mCLen[LoopVar1] == 0) {
        LoopVar1++;
        Count++;
      }

      if (Count <= 2) {
        for (LoopVar3 = 0; LoopVar3 < Count; LoopVar3++) {
          PutBits (mPTLen[0], mPTCode[0]);
        }
      } else if (Count <= 18) {
        PutBits (mPTLen[1], mPTCode[1]);
        PutBits (4, Count - 3);
      } else if (Count == 19) {
        PutBits (mPTLen[0], mPTCode[0]);
        PutBits (mPTLen[1], mPTCode[1]);
        PutBits (4, 15);
      } else {
        PutBits (mPTLen[2], mPTCode[2]);
        PutBits (CBIT, Count - 20);
      }
    } else {
      ASSERT ((LoopVar3+2) < NPT);
      PutBits (mPTLen[LoopVar3 + 2], mPTCode[LoopVar3 + 2]);
    }
  }
}

/**
  Huffman code the block and output it.

**/
VOID
SendBlock (
  VOID
  )
{
  UINT32  LoopVar1;

  UINT32  LoopVar3;

  UINT32  Flags;

  UINT32  Root;

  UINT32  Pos;

  UINT32  Size;

  Flags = 0;

  Root = MakeTree (NC, mCFreq, mCLen, mCCode);
  Size = mCFreq[Root];
  PutBits (16, Size);
  if (Root >= NC) {
    CountTFreq ();
    Root = MakeTree (NT, mTFreq, mPTLen, mPTCode);
    if (Root >= NT) {
      WritePTLen (NT, TBIT, 3);
    } else {
      PutBits (TBIT, 0);
      PutBits (TBIT, Root);
    }

    WriteCLen ();
  } else {
    PutBits (TBIT, 0);
    PutBits (TBIT, 0);
    PutBits (CBIT, 0);
    PutBits (CBIT, Root);
  }

  Root = MakeTree (NP, mPFreq, mPTLen, mPTCode);
  if (Root >= NP) {
    WritePTLen (NP, PBIT, -1);
  } else {
    PutBits (PBIT, 0);
    PutBits (PBIT, Root);
  }

  Pos = 0;
  for (LoopVar1 = 0; LoopVar1 < Size; LoopVar1++) {
    if (LoopVar1 % UINT8_BIT == 0) {
      Flags = mBuf[Pos++];
    } else {
      Flags <<= 1;
    }

    if ((Flags & (1U << (UINT8_BIT - 1))) != 0) {
      EncodeC (mBuf[Pos++] + (1U << UINT8_BIT));
      LoopVar3  = mBuf[Pos++] << UINT8_BIT;
      LoopVar3 += mBuf[Pos++];

      EncodeP (LoopVar3);
    } else {
      EncodeC (mBuf[Pos++]);
    }
  }

  SetMem (mCFreq, NC * sizeof (UINT16), 0);
  SetMem (mPFreq, NP * sizeof (UINT16), 0);
}

/**
  Start the huffman encoding.

**/
VOID
HufEncodeStart (
  VOID
  )
{
  SetMem (mCFreq, NC * sizeof (UINT16), 0);
  SetMem (mPFreq, NP * sizeof (UINT16), 0);

  mOutputPos = mOutputMask = 0;

  mBitCount  = UINT8_BIT;
  mSubBitBuf = 0;
}

/**
  Outputs an Original Character or a Pointer.

  @param[in] LoopVar5     The original character or the 'String Length' element of
                   a Pointer.
  @param[in] LoopVar7     The 'Position' field of a Pointer.
**/
VOID
CompressOutput (
  IN UINT32  LoopVar5,
  IN UINT32  LoopVar7
  )
{
  STATIC UINT32  CPos;

  if ((mOutputMask >>= 1) == 0) {
    mOutputMask = 1U << (UINT8_BIT - 1);
    if (mOutputPos >= mBufSiz - 3 * UINT8_BIT) {
      SendBlock ();
      mOutputPos = 0;
    }

    CPos       = mOutputPos++;
    mBuf[CPos] = 0;
  }

  mBuf[mOutputPos++] = (UINT8)LoopVar5;
  mCFreq[LoopVar5]++;
  if (LoopVar5 >= (1U << UINT8_BIT)) {
    mBuf[CPos]         = (UINT8)(mBuf[CPos]|mOutputMask);
    mBuf[mOutputPos++] = (UINT8)(LoopVar7 >> UINT8_BIT);
    mBuf[mOutputPos++] = (UINT8)LoopVar7;
    LoopVar5           = 0;
    while (LoopVar7 != 0) {
      LoopVar7 >>= 1;
      LoopVar5++;
    }

    mPFreq[LoopVar5]++;
  }
}

/**
  End the huffman encoding.

**/
VOID
HufEncodeEnd (
  VOID
  )
{
  SendBlock ();

  //
  // Flush remaining bits
  //
  PutBits (UINT8_BIT - 1, 0);
}

/**
  The main controlling routine for compression process.

  @retval EFI_SUCCESS           The compression is successful.
  @retval EFI_OUT_0F_RESOURCES  Not enough memory for compression process.
**/
EFI_STATUS
Encode (
  VOID
  )
{
  EFI_STATUS  Status;
  INT32       LastMatchLen;
  NODE        LastMatchPos;

  Status = AllocateMemory ();
  if (EFI_ERROR (Status)) {
    FreeMemory ();
    return Status;
  }

  InitSlide ();

  HufEncodeStart ();

  mRemainder = FreadCrc (&mText[WNDSIZ], WNDSIZ + MAXMATCH);

  mMatchLen = 0;
  mPos      = WNDSIZ;
  InsertNode ();
  if (mMatchLen > mRemainder) {
    mMatchLen = mRemainder;
  }

  while (mRemainder > 0) {
    LastMatchLen = mMatchLen;
    LastMatchPos = mMatchPos;
    if (!GetNextMatch ()) {
      Status = EFI_OUT_OF_RESOURCES;
    }

    if (mMatchLen > mRemainder) {
      mMatchLen = mRemainder;
    }

    if ((mMatchLen > LastMatchLen) || (LastMatchLen < THRESHOLD)) {
      //
      // Not enough benefits are gained by outputting a pointer,
      // so just output the original character
      //
      CompressOutput (mText[mPos - 1], 0);
    } else {
      //
      // Outputting a pointer is beneficial enough, do it.
      //

      CompressOutput (
        LastMatchLen + (UINT8_MAX + 1 - THRESHOLD),
        (mPos - LastMatchPos - 2) & (WNDSIZ - 1)
        );
      LastMatchLen--;
      while (LastMatchLen > 0) {
        if (!GetNextMatch ()) {
          Status = EFI_OUT_OF_RESOURCES;
        }

        LastMatchLen--;
      }

      if (mMatchLen > mRemainder) {
        mMatchLen = mRemainder;
      }
    }
  }

  HufEncodeEnd ();
  FreeMemory ();
  return (Status);
}

/**
  The compression routine.

  @param[in]       SrcBuffer     The buffer containing the source data.
  @param[in]       SrcSize       Number of bytes in SrcBuffer.
  @param[in]       DstBuffer     The buffer to put the compressed image in.
  @param[in, out]  DstSize       On input the size (in bytes) of DstBuffer, on
                                 return the number of bytes placed in DstBuffer.

  @retval EFI_SUCCESS           The compression was successful.
  @retval EFI_BUFFER_TOO_SMALL  The buffer was too small.  DstSize is required.
**/
EFI_STATUS
Compress (
  IN      VOID    *SrcBuffer,
  IN      UINT64  SrcSize,
  IN      VOID    *DstBuffer,
  IN OUT  UINT64  *DstSize
  )
{
  EFI_STATUS  Status;

  //
  // Initializations
  //
  mBufSiz     = 0;
  mBuf        = NULL;
  mText       = NULL;
  mLevel      = NULL;
  mChildCount = NULL;
  mPosition   = NULL;
  mParent     = NULL;
  mPrev       = NULL;
  mNext       = NULL;

  mSrc           = SrcBuffer;
  mSrcUpperLimit = mSrc + SrcSize;
  mDst           = DstBuffer;
  mDstUpperLimit = mDst +*DstSize;

  PutDword (0L);
  PutDword (0L);

  MakeCrcTable ();

  mOrigSize = mCompSize = 0;
  mCrc      = INIT_CRC;

  //
  // Compress it
  //
  Status = Encode ();
  if (EFI_ERROR (Status)) {
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // Null terminate the compressed data
  //
  if (mDst < mDstUpperLimit) {
    *mDst++ = 0;
  }

  //
  // Fill in compressed size and original size
  //
  mDst = DstBuffer;
  PutDword (mCompSize + 1);
  PutDword (mOrigSize);

  //
  // Return
  //
  if (mCompSize + 1 + 8 > *DstSize) {
    *DstSize = mCompSize + 1 + 8;
    return EFI_BUFFER_TOO_SMALL;
  } else {
    *DstSize = mCompSize + 1 + 8;
    return EFI_SUCCESS;
  }
}
